package ufrj.safcp.tools.qrcode;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.zxing.qrcode.QRCodeWriter;

/**
 * 
 * Classe que compacta e descompacta as informações do QRCode usando zlib.
 * 
 * @author Bruno Medeiros
 *
 */
public class ZlibCompressorQRCodeDataDecorator implements IQRCodeHandler {
	
	private static Logger logger = LoggerFactory.getLogger(ZlibCompressorQRCodeDataDecorator.class);

	private IQRCodeHandler decorated;

	public ZlibCompressorQRCodeDataDecorator(IQRCodeHandler decorated) {
		super();
		this.decorated = decorated;
	}
	
	@Override
	public BufferedImage encode(byte[] contents) {
		
		if (logger.isDebugEnabled()) {
			StringBuffer sb = new StringBuffer();
			sb.append("{");
			boolean first = true;
			for (byte b : contents) {
				if (first) {
					first = false;
				} else {
					sb.append(", ");
				}
				sb.append(b);
			}
			sb.append("}");
			logger.debug("Compressing byte[] " + sb);
		}
		QRCodeWriter codeWriter = new QRCodeWriter();
		
		// Compress the bytes
		Deflater compressor = new Deflater();
		compressor.setLevel(Deflater.BEST_COMPRESSION);
		compressor.setStrategy(Deflater.FILTERED);
		compressor.setInput(contents);
		compressor.finish();
		
		ByteArrayOutputStream bos = new ByteArrayOutputStream(contents.length);

		// Compress the data
		byte[] buf = new byte[1024];
		while (!compressor.finished()) {
		    int count = compressor.deflate(buf);
		    bos.write(buf, 0, count);
		}
		try {
		    bos.close();
		} catch (IOException e) {
		}

		// Get the compressed data
		byte[] compressedData = bos.toByteArray();
		
		if (logger.isDebugEnabled()) {
			logger.debug("Compressed byte[] of " + contents.length + " bytes using zlib, compress ratio  = "
					+ (compressedData.length * 1.0 / contents.length));
		}
		return this.decorated.encode(compressedData);
	}

	@Override
	public byte[] decode(Image contents) throws UnredableCodeException {
		byte[] decoded = this.decorated.decode(contents);

		Inflater decompresser = new Inflater();
		decompresser.setInput(decoded);
		byte[] result = new byte[2000];
		int resultLength;
		try {
			resultLength = decompresser.inflate(result);
		} catch (DataFormatException e) {
			return decoded; // Retornando array normal caso não consiga descriptografar
		}
		decompresser.end();

		return Arrays.copyOf(result, resultLength);
	}

}
